import asyncio
from typing import Dict, Optional
from sqlalchemy.orm import Session
from app.models.database.models import ServiceConfig, SwitchLog
from app.services.monitoring.failure_detector import failure_detector
from app.services.notification.notification_service import notification_service
from app.services.health.health_checker import health_checker
import logging
from datetime import datetime

logger = logging.getLogger(__name__)


class SwitchManager:
    """灾备切换管理服务"""

    def __init__(self):
        self.switch_locks = {}  # 防止并发切换

    async def perform_switch(
            self,
            service_id: int,
            target_status: str,
            switch_type: str = "manual",
            reason: Optional[str] = None,
            operator: Optional[str] = None,
            db: Session = None
    ) -> Dict[str, any]:
        """执行灾备切换"""

        # 检查是否正在切换中
        if service_id in self.switch_locks and self.switch_locks[service_id]:
            return {
                "success": False,
                "message": "Switch already in progress",
                "switch_log": None
            }

        # 设置切换锁
        self.switch_locks[service_id] = True

        try:
            # 获取服务配置
            service = db.query(ServiceConfig).filter(ServiceConfig.id == service_id).first()
            if not service:
                return {
                    "success": False,
                    "message": "Service not found",
                    "switch_log": None
                }

            # 确定当前状态和目标状态
            current_status = "primary" if service.is_active else "backup"

            if current_status == target_status:
                return {
                    "success": False,
                    "message": f"Already in {target_status} mode",
                    "switch_log": None
                }

            # 验证目标服务健康状态
            if target_status == "primary":
                target_url = service.primary_url
            else:
                target_url = service.backup_url

            is_healthy = failure_detector.check_recovery_status(
                service_id, target_url, db
            )

            if not is_healthy:
                return {
                    "success": False,
                    "message": f"Target service ({target_status}) is not healthy",
                    "switch_log": None
                }

            # 执行切换
            switch_result = await self._execute_switch(
                service, target_status, switch_type, reason, operator, db
            )

            return switch_result

        finally:
            # 释放切换锁
            self.switch_locks[service_id] = False

    async def _execute_switch(
            self,
            service: ServiceConfig,
            target_status: str,
            switch_type: str,
            reason: Optional[str],
            operator: Optional[str],
            db: Session
    ) -> Dict[str, any]:
        """执行实际的切换操作"""

        from_status = "primary" if service.is_active else "backup"

        try:
            # 记录切换开始
            switch_log = SwitchLog(
                service_id=service.id,
                switch_type=switch_type,
                from_status=from_status,
                to_status=target_status,
                reason=reason,
                operator=operator,
                success=True
            )

            # 更新服务状态
            service.is_active = (target_status == "primary")
            service.updated_at = datetime.utcnow()

            # 保存到数据库
            db.add(switch_log)
            db.commit()
            db.refresh(switch_log)

            # 发送通知
            await notification_service.send_switch_notification(
                service=service,
                from_status=from_status,
                to_status=target_status,
                reason=reason,
                operator=operator
            )

            logger.info(
                f"Switch completed for service {service.name}: "
                f"{from_status} -> {target_status}"
            )

            return {
                "success": True,
                "message": f"Successfully switched to {target_status}",
                "switch_log": switch_log
            }

        except Exception as e:
            db.rollback()

            # 记录失败的切换
            failed_switch_log = SwitchLog(
                service_id=service.id,
                switch_type=switch_type,
                from_status=from_status,
                to_status=target_status,
                reason=reason,
                operator=operator,
                success=False,
                error_message=str(e)
            )
            db.add(failed_switch_log)
            db.commit()

            logger.error(f"Switch failed for service {service.name}: {e}")

            return {
                "success": False,
                "message": f"Switch failed: {str(e)}",
                "switch_log": failed_switch_log
            }

    async def auto_switch_on_failure(
            self,
            service_id: int,
            failure_reason: str,
            db: Session
    ) -> Dict[str, any]:
        """故障时自动切换"""

        service = db.query(ServiceConfig).filter(ServiceConfig.id == service_id).first()
        if not service:
            return {"success": False, "message": "Service not found"}

        # 确定切换目标
        current_status = "primary" if service.is_active else "backup"
        target_status = "backup" if current_status == "primary" else "primary"

        return await self.perform_switch(
            service_id=service_id,
            target_status=target_status,
            switch_type="automatic",
            reason=f"Auto-switch due to failure: {failure_reason}",
            operator="system",
            db=db
        )

    def get_switch_history(
            self,
            service_id: int,
            limit: int = 50,
            db: Session = None
    ) -> list:
        """获取切换历史"""

        switches = db.query(SwitchLog) \
            .filter(SwitchLog.service_id == service_id) \
            .order_by(SwitchLog.created_at.desc()) \
            .limit(limit) \
            .all()

        return switches

    def get_current_status(self, service_id: int, db: Session) -> Dict[str, any]:
        """获取当前服务状态"""

        service = db.query(ServiceConfig).filter(ServiceConfig.id == service_id).first()
        if not service:
            return {"error": "Service not found"}

        # 获取最新切换记录
        latest_switch = db.query(SwitchLog) \
            .filter(SwitchLog.service_id == service_id) \
            .order_by(SwitchLog.created_at.desc()) \
            .first()

        # 获取健康状态
        health_analysis = failure_detector.analyze_service_health(service_id, db)

        return {
            "service_id": service.id,
            "service_name": service.name,
            "current_status": "primary" if service.is_active else "backup",
            "health_status": health_analysis["latest_status"],
            "last_switch": latest_switch.created_at if latest_switch else None,
            "last_switch_reason": latest_switch.reason if latest_switch else None,
            "total_switches": db.query(SwitchLog) \
                .filter(SwitchLog.service_id == service_id) \
                .count(),
            "uptime": failure_detector.get_service_uptime(service_id, db=db)
        }

    async def bulk_switch(
            self,
            service_ids: list,
            target_status: str,
            reason: str,
            operator: str,
            db: Session
    ) -> Dict[str, list]:
        """批量切换服务"""

        results = {
            "successful": [],
            "failed": []
        }

        # 并发执行切换
        tasks = []
        for service_id in service_ids:
            task = self.perform_switch(
                service_id=service_id,
                target_status=target_status,
                switch_type="manual",
                reason=reason,
                operator=operator,
                db=db
            )
            tasks.append(task)

        switch_results = await asyncio.gather(*tasks, return_exceptions=True)

        for service_id, result in zip(service_ids, switch_results):
            if isinstance(result, Exception):
                results["failed"].append({
                    "service_id": service_id,
                    "error": str(result)
                })
            elif result["success"]:
                results["successful"].append({
                    "service_id": service_id,
                    "switch_log": result["switch_log"]
                })
            else:
                results["failed"].append({
                    "service_id": service_id,
                    "error": result["message"]
                })

        return results


# 全局切换管理器实例
switch_manager = SwitchManager()